package com.liu.queue.storage;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashSet;
import java.util.Iterator;

/**
 * @param
 * @BelongsProject: QueueDemo2
 * @BelongsPackage: com.liu.queue.cache
 * @author: L
 * @Date: 2023/10/16 15:44
 * @return
 */

@Slf4j
public class StorageFind {
    private static volatile Storage storage;

    public static Storage getStorage(String cachePath)  {
        if (storage == null) {
            synchronized (StorageFind.class) {
                if (storage == null) {
                    storage = findByPath(cachePath);
                }
            }
        }
        return storage;
    }

    private static Storage findByPath(String cachePath) {
        String scanPath = cachePath == null ? "com.liu.queue.storage.impl.OrdinaryStorage" : cachePath;
        // 如果指定类类,走这里
        try {
            Class<?> clazz = Class.forName(scanPath);
            Storage storageInstance = getStorage(clazz);
            if (storageInstance == null) {
                throw new RuntimeException("指定类不存在");
            }
            return storageInstance;
        } catch (ClassNotFoundException e) {

        }
        String scanUrl = scanPath.replace('.', '/');
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        // 获取URL
        URL resource = loader.getResource(scanUrl);
        String absolutePath = resource.getPath().substring(1);
        HashSet<Class<?>> classeSet = new HashSet<>();
        try {
            Files.walkFileTree(Paths.get(absolutePath), new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    String filePath = file.toUri().getPath();
                    if (filePath.endsWith(".class")) {
                        int i1 = filePath.indexOf(scanUrl);
                        int i2 = filePath.lastIndexOf('.');
                        String substring = filePath.substring(i1, i2).replace('/', '.');
                        Class<?> clazz = null;
                        try {
                            clazz = Class.forName(substring);
                        } catch (ClassNotFoundException e) {
                            throw new RuntimeException(e);
                        }
                        classeSet.add(clazz);
                    }
                    return super.visitFile(file, attrs);
                }
            });
        } catch (IOException e) {
            throw new RuntimeException("没有对应文件");
        }
        Iterator<Class<?>> iterator = classeSet.iterator();
        while (iterator.hasNext()) {
            Class<?> next = iterator.next();
            Storage storageInstance = getStorage(next);
            if (storageInstance != null) {
                return storageInstance;
            }
        }
        throw new RuntimeException("没有空参构造器或没有找到Cache接口的实现类");
    }

    private static Storage getStorage(Class<?> next) {
        if (next.isInterface() || !findInterface(next)) {
            return null;
        }
        Constructor<?>[] constructors = next.getConstructors();
        for (Constructor constructor : constructors) {
            if (constructor.getParameterCount() == 0) {
                try {
                    return (Storage) constructor.newInstance();
                } catch (InstantiationException e) {
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        Constructor<?>[] declaredConstructors = next.getDeclaredConstructors();
        for (Constructor<?> constructor : declaredConstructors) {
            if (constructor.getParameterCount() == 0) {
                try {
                    constructor.setAccessible(true);
                    return (Storage) constructor.newInstance();
                } catch (InstantiationException e) {
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return null;
    }

    private static boolean findInterface(Class<?> clazz) {
        if (clazz.isAssignableFrom(Object.class)) {
            return false;
        }
        if (clazz.isAssignableFrom(Storage.class)) {
            return true;
        }
        Class<?>[] interfaces = clazz.getInterfaces();
        for (Class<?> superInterface : interfaces) {
            return findInterface(superInterface);
        }
        Class<?> superclass = clazz.getSuperclass();
        return findInterface(superclass);
    }

}
